#include "app_blue.h"

#include "app_serial.h"

/*============ 回调函数 ==========================*/

/* 接受蓝牙数据并不是一次就可以接收到正确的数据，需要反复判断
   1. 需要一个临时缓冲区来存放接收到的数据
   2. 需要一个长度来表示接收到的实际数据
*/
static char blue_buff[1024];
static int blue_buff_len = 0;

/* parm : 从哪里开始移 */
static void move_recBlueData(int i)
{
    /* parm1:蓝牙buff首地址，parm2：移动位置（要删除的数据长度），parm3: 移动长度 ：总长度-要删除的数据长度 */
    memmove(blue_buff, blue_buff + i, blue_buff_len - i);
    blue_buff_len -= i; /* */
}

/**
 * @brief 将蓝牙数据转换为字符数组
 * 字符数组格式：
 *        conn_type : 1个字节  buff[0]
 *        id_len:  1个字节     buff[1]
 *        data_len: 1个字节    buff[2]
 *        id:       2个字节    buff[3] ~ buff[4]
 *        data: x             buff[5] ~ buff[5+data_len]
 *
 * 接收蓝牙帧格式：
 *       接收方得到数据（3 + [2]）：f1 dd 07 23 23 ff ff 41 42 43
         f1 dd : 固定的头部
            07： 之后数据的长度（5-16之间）
         23 23：对端（发送方）的MADDR
         ff ff: 我的MADDR或ffff(群发)
         41 42 43：发送的数据
     处理后的数据格式：conn_type id_len msg_len id msg
 *
 * @param data
 * @param date_len
 * @return int
 */
int app_blue_post_read(char *data, int data_len)
{
    /* 1. 先将接收到的数据拷贝到临时缓冲区 */
    memcpy(blue_buff + blue_buff_len, data, data_len);
    blue_buff_len += data_len;

    char fix_head[2] = {0xf1, 0xdd};

    /* 2.看接收到的蓝牙数据帧是否满足最小数据长度:8 */
    if (blue_buff_len < 8)
    {
        log_debug("接收到的蓝牙数据帧长度小于8,直接返回");
        return -1; /**/
    }

    /* 3.开始处理蓝牙数据帧 */
    int i = 0;
    for (i = 0; i < blue_buff_len - 7; i++)
    {
        /* 帧头是固定的，
           遍历比较收到的蓝牙帧和固定的帧头是否相同（因为无线传输不是很稳定，可能有垃圾数据）
           这里的 blue_buff_len - 7 是为了保证遍历到最后的数据至少是一个最小数据帧
           如果相同，走这个分支
        */
        if (memcmp(fix_head, blue_buff + i, 2) == 0)
        {
            /* 以当前位置 i 为轴，i之前的为无效数据，i之后的为有效数据
               删除i之前的无效数据
            */
            move_recBlueData(i);

            /* 到这里就有第一个有效数据了，但是可能不完整 */
            int target_len = 2 + 1 + blue_buff[2]; /* 目标长度 = 2(帧头长度) + 1（数据长度） + 数据  */
            if (blue_buff_len < target_len)
            {
                log_debug("接收到的蓝牙数据帧长度小于目标长度,直接返回");
                return -1;
            }

            /* 如果到这里，就是正确的数据了，然后就是拼接字符数组
               处理后的数据格式：conn_type+ id_len +msg_len +id + msg
              */
            memset(data, 0, data_len);
            data[0] = 1;
            data[1] = 2;
            data[2] = blue_buff[2] - 4; /* 注意这里是纯数据长度，蓝牙帧长度还包含MAC地址 */
            memcpy(data + 3, blue_buff + 3, 2);
            memcpy(data + 5, blue_buff + 7, data[2]);

            /* 删除已经处理过的蓝牙数据*/
            move_recBlueData(target_len);

            /* 返回长度 */
            return 5 + data[2];
        }
        else
        {
            /* 如果不同进行下一次判断*/
            continue;
        }
    }

    /* 如果走到这，说明遍历完了还是没找到有效枕头数据，那就把i之前的数据全删了 */
    log_debug("遍历完了还是没找到有效枕头数据");
    move_recBlueData(i);

    return -1;
}

/**
 * @brief 将字符数组转化为蓝牙帧
 *        蓝牙帧格式：
 *       帧头：AT+MESH  (41 54 2b 4d 45 53 48 00 == 8BYTE)
 *       ID: FF FF   (ff ff == 2BYTE)
 *       数据： x    (不确定)
 *       帧尾：\r\n   (0d 0a == 2BYTE)
 *
 * @param data
 * @param date_len
 * @return int
 */
int app_blue_pre_write(char *str_buff, int strBuff_len)
{
    /* 0.字符数据长度合法性判断   */
    if (strBuff_len < 6)
    {
        log_error("字符数组长度小于6");
        return -1;
    }

    /* 1.创建临时缓冲区存放蓝牙帧 */
    int blue_len = 12 + str_buff[2]; /* 蓝牙帧固定长度12 + 数据长度 */
    char blue_buff[blue_len];
    // char blue_buff[blue_len] = {0};
    /* 2. 组建蓝牙帧 */
    memcpy(blue_buff, "AT+MESH", 8); /* 7 + '\0'*/
    memcpy(blue_buff + 8, str_buff + 3, 2);
    memcpy(blue_buff + 10, str_buff + 5, str_buff[2]);
    memcpy(blue_buff + 12 + str_buff[2], "\r\n", 2);

    /* 3.清空data */
    memset(str_buff, 0, strBuff_len);

    /* 4.将组建的蓝牙帧拷贝到data */
    memcpy(str_buff, blue_buff, blue_len);
    memset(blue_buff, 0, blue_len);

    /* 5.返回蓝牙帧长度 */
    return blue_len;
}

/*============ 回调函数 ==========================*/

returnType_t blue_init(Device *device)
{
    /* 1.串口模块初始化 */
    app_serial_init(device);
    app_serial_isblock(device, NONBLOCKING); /* 设置为非阻塞模式 */

    /* 2.连通性测试   */
    if (app_bt_status(device) == ERROR)
    {
        log_error("蓝牙初始化失败");
        return ERROR;
    }

    /* 3.蓝牙细节参数配置 */
    app_bt_rename(device, "blue eyes");  // 设置蓝牙名称
    app_bt_setNetId(device, "1234"); // 设置组网ID: 组内相同，组间不同
    app_bt_setMaddr(device, "1234"); // 设置MAC地址：组内不同，组间可以相同

    // 将串口设置为阻塞模式
    app_serial_isblock(device, BLOCKING);

    return SUCCESS;
}

/**
 * @brief 蓝牙初始化
 *
 */
returnType_t app_blue_init(Device *device)
{
    /* 1.注册两个回调函数 */
    device->post_read = app_blue_post_read;
    device->pre_write = app_blue_pre_write;

    /* 2.蓝牙的初始化 */
    blue_init(device);

    return SUCCESS;
}

/**
 * 判断是否收到ACK指令
 */
returnType_t wait_ack(int fd)
{
    // 等待一定的时间
    usleep(50 * 1000);
    // 读取数据
    char data_buf[4];
    read(fd, data_buf, 4);
    // 判断是否是OK\r\n
    if (memcmp(data_buf, "OK\r\n", 4) == -1)
    {
        log_error("没有收到ACK指令");
        return ERROR;
    }

    log_debug("收到ACK指令");
    return SUCCESS;
}

/**
 * 判断蓝牙是否可用
 */
returnType_t app_bt_status(Device *device)
{
    // 向蓝牙串口文件写入“AT\r\n”的指令数据
    write(device->fd, "AT\r\n", 4);
    // 通过读取“OK\r\n”数据来判断蓝牙是否可用
    return wait_ack(device->fd);
}

/**
 * 修改名称
 */
returnType_t app_bt_rename(Device *device, char *name)
{
    // 拼接指令
    char cmd[20];
    sprintf(cmd, "AT+NAME%s\r\n", name);
    // 写入指令
    write(device->fd, cmd, strlen(cmd));
    // 等待ACK
    return wait_ack(device->fd);
}

/**
 * 设置波特率
 */
returnType_t app_bt_setBaudRate(Device *device, char baudRate)
{
    // 拼接指令
    char cmd[20];
    sprintf(cmd, "AT+BAUD%c\r\n", baudRate);
    // 写入指令
    write(device->fd, cmd, strlen(cmd));
    // 等待ACK
    return wait_ack(device->fd);
}

/**
 * 重启蓝牙
 */
returnType_t app_bt_reset(Device *device)
{
    // 指令
    char *cmd = "AT+RESET\r\n";
    // 写入指令
    write(device->fd, cmd, strlen(cmd));
    // 等待ACK
    return wait_ack(device->fd);
}

/**
 * 设置组网ID  "a3a4"
 */
returnType_t app_bt_setNetId(Device *device, char *netid)
{
    // 拼接指令
    char cmd[20];
    sprintf(cmd, "AT+NETID%s\r\n", netid);
    // 写入指令
    write(device->fd, cmd, strlen(cmd));
    // 等待ACK
    return wait_ack(device->fd);
}

/**
 * 设置Mac地址  "0101"
 */
returnType_t app_bt_setMaddr(Device *device, char *maddr)
{
    // 拼接指令
    char cmd[20];
    sprintf(cmd, "AT+MADDR%s\r\n", maddr);
    // 写入指令
    write(device->fd, cmd, strlen(cmd));
    // 等待ACK
    return wait_ack(device->fd);
}
